#pragma once

#include <iostream>
#include <unistd.h>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <algorithm>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <memory>

#include "util.hpp"
#include "log_pro.hpp"

using std::cin;
using std::cout;
using std::endl;

// #define DEBUG
static const std::string &Sep = ": ";                // 请求报头中属性名与属性的分割符
static const std::string &WebRoot = "../webRoot";    // web根目录名
static const std::string &HomePage = "/index.html";  // 文件下的首页
static const std::string &Http_Version = "HTTP/1.0"; // 响应报文版本号
static const std::string &Rseponse_End = "\r\n";     // 响应报文的行分隔符
static const std::string &page_404 = "/404.html";    // 响应报文的行分隔符

// 状态码
static const std::string &Not_Find = "404";     // 错误的状态码
static const std::string &OK = "200";           // 正确的状态码
static const std::string &Server_Error = "500"; // 服务器错误的状态码
static const std::string &Bad_Request = "400";  // 请求错误的状态码

class HttpRequest
{
public:
    std::string _request_line;
    std::vector<std::string> _request_head;
    std::string _blank;
    std::string _request_body;

    // 首行中各属性
    std::string _method;
    std::string _uri;
    std::string _version;

    // 请求报头中属性名与属性的映射表
    std::unordered_map<std::string, std::string> _attribute_map;

    // 正文部分的长度
    int _Content_Length = 0;

    // 请求行uri中请求目标文件与请求附加的属性
    std::string _path;
    std::string _submit_attribute; // 提交属性
    std::string _suffix;           // 目标文件的后缀

    // CGI机制
    bool _cgi = false;
};

class HttpResponse
{
public:
    std::string _status_line;
    std::vector<std::string> _response_head;
    std::string _blank;
    std::string _response_body;

    // 状态行成员
    std::string _code;          // 状态码
    std::string _code_describe; // 状态码描述
    std::string _version;       // HTTP版本号

    // 响应报文文件描述符
    int _fd = -1;
    int _bodySize = 0; // 文件的大小
};

class Dispose
{
private:
    int _sock;
    HttpRequest _req;
    HttpResponse _rep;

    bool recvHttpRequestLine() // 读取请求行
    {
        // 读取首行
        std::string &line = _req._request_line;
        if (Util::ReadLine(_sock, line) > 0)
        {
            line.resize(line.size() - 1);

            //  //debug
            //  lg(DEBUG, line.c_str());
        }
        else
        {
            // 读取出错
            _stop = true;
        }
        return _stop;
    }
    bool recvHttpRequestHead() // 读取报头属性
    {
        while (true)
        {
            std::string line;
            if (Util::ReadLine(_sock, line) <= 0)
            {
                // 读取出错
                _stop = true;
                break;
            }
            if (line == "\n")
            {
                _req._blank = line;
                break;
            }
            line.resize(line.size() - 1);
            _req._request_head.push_back(line);
        }

        // //debug
        // for (auto &it : _req._request_head)
        // {
        //     lg(DEBUG, it.c_str());
        // }
        // lg(DEBUG, _req._blank.c_str());

        return _stop;
    }
    void parseHttpRequestLine() // 处理请求行
    {
        // 将首行数据拆分出出来
        std::stringstream str(_req._request_line);
        str >> _req._method >> _req._uri >> _req._version;

        // 将method中的内容全部转换为大写，防止不同浏览器请求报文不同
        std::transform(_req._method.begin(), _req._method.end(), _req._method.begin(), ::toupper);

        // //debug
        // lg(DEBUG, _req._method.c_str());
        // lg(DEBUG, _req._uri.c_str());
        // lg(DEBUG, _req._version.c_str());
    }
    void parseHttpRequestHead() // 处理请求报头
    {
        std::string key;
        std::string value;
        for (auto &it : _req._request_head)
        {
            bool ret = Util::cutString(it, key, value, Sep);
            if (ret == false)
            {
                lg(WARNING, "parseHttpRequestHead cutString fail!");
            }

            _req._attribute_map[key] = value;
        }

        // // debug
        // for (auto &it : _req._attribute_map)
        // {
        //     lg(DEBUG, it.first.c_str());
        //     lg(DEBUG, it.second.c_str());
        // }
    }

    bool IsNeedRecvHttpRequestBody() // 检测是否需要读取正文
    {
        if (_req._method == "POST")
        {
            auto it = _req._attribute_map.find("Content-Length");
            if (it != _req._attribute_map.end())
            {
                _req._Content_Length = stoi(it->second); // 设置_Content_Length属性为下面的读取正文做准备
                return true;
            }
        }
        return false;
    }

    bool recvHttpRequestBody() // 读取正文
    {
        if (IsNeedRecvHttpRequestBody())
        {
            std::string &body = _req._request_body;
            int length = _req._Content_Length;
            char ch = '\0';
            while (length)
            {
                if (recv(_sock, &ch, 1, 0) > 0)
                {
                    body.push_back(ch);
                    length--;
                }
                else
                {
                    // 如果读取出错
                    _stop = true;
                    break;
                }
            }
            // debug
            lg(DEBUG, body.c_str());
        }
        return _stop;
    }

    std::string codeToDescribe(int code)
    {
        switch (code)
        {
        case 200:
            return "OK";
            break;
        case 404:
            return "Not Found";
            break;
        case 400:
            return "Bad Request";
            break;
        case 500:
            return "Server Error";
            break;
        default:
            return "Defualt";
            break;
        }
    }

    std::string suffixToType(const std::string &str)
    {
        static const std::unordered_map<std::string, std::string> suffixToTypeMap = {
            {".html", "text/html"},
            {".css", "text/css"},
            {".svg", "text/xml"},
            {".png", "image/png"},
            {".js", "application/x-javascript"}};
        auto it = suffixToTypeMap.find(str);
        if (it != suffixToTypeMap.end())
        {
            return it->second;
        }
        lg(WARNING, "not found suffix!");
        return "text/html";
    }

    std::string nonCgiToRequest() // 只起到打开静态网页文件的作用
    {
        // lg(DEBUG, "comming nonCgiToRequest!");
        _rep._fd = open(_req._path.c_str(), O_RDONLY);
        if (_rep._fd >= 0) // 只有打开目标路径文件成功时才继续向下制作报文
        {
            return OK;
        }
        else
        {
            lg(WARNING, "poen file fail!");
        }
        return Not_Find;
    }

    std::string cgiToRequest() // 使用CGI方式处理请求
    {
        lg(DEBUG, "comming cgiToRequest");
        int input[2];  // 读管道
        int output[2]; // 写管道

        std::string code; // 返回的响应码

        // 只会从下面两个内容中获取数据
        std::string &body = _req._request_body;          // POST方法时使用(使用write写入管道)
        std::string &attribute = _req._submit_attribute; // GET方法时使用(使用环境变量设置)

        // 需要用到的环境变量字符(在这里提前声明的原因是因为putenv这个函数的处理只是将环境变量只想这些字符不是拷贝)
        std::string method = "Method=" + _req._method;
        std::string SubmitAttribute = "SubmitAttribute=" + attribute;
        std::string Content_Length = "ContentLength=" + std::to_string(_req._Content_Length);

        // 创建两个管道
        if (pipe(input) < 0)
        {
            lg(ERROR, "pipe input create fail!");
            return Server_Error;
        }
        if (pipe(output) < 0)
        {
            lg(ERROR, "pipe output create fail!");
            return Server_Error;
        }

        pid_t pid = fork();
        if (pid == 0) // 子进程
        {
            // 首先关闭不需要的文件描述符
            close(input[0]);
            close(output[1]);

            // 通过环境变量告诉替换进程，采用的是GET还是POST方法提供的数据
            putenv((char *)method.c_str());

            if ("GET" == _req._method) // GET方法通过设置环境变量方式将数据传递给替换掉的进程
            {
                putenv((char *)SubmitAttribute.c_str());
                lg(DEBUG, SubmitAttribute.c_str());
            }

            if ("POST" == _req._method)
            {
                putenv((char *)Content_Length.c_str());
            }

            // 再将管道文件描述符重定向
            dup2(input[1], 1);
            dup2(output[0], 0);

            // exec进程替换
            if (execl(_req._path.c_str(), _req._path.c_str(), nullptr) == -1)
            {
                exit(-1);
            }
            exit(0);
        }
        else if (pid > 0) // 父进程
        {
            close(input[1]);
            close(output[0]);

            if ("POST" == _req._method) // post方法通过write写入发送给子进程
            {
                int size = 0;
                int total = 0;
                while ((total < body.size()) && (size = write(output[1], body.c_str() + total, body.size() - total)) > 0)
                {
                    total += size;
                }
                close(output[1]);
            }

            // 开始等待读取exec进程处理后的数据
            while (true)
            {
                char buffer[1024];
                int n = read(input[0], buffer, sizeof(buffer) - 1);
                if (n == 0)
                {
                    break; // 读完了
                }
                else if (n < 0)
                {
                    std::cout << "read fail" << std::endl; // 读出错
                    break;
                }
                else
                {
                    buffer[n] = '\0';
                    _rep._response_body += buffer; // 将数据放到响应的body中
                }
            }
            _rep._bodySize = _rep._response_body.size(); // 修改一下报文body的大小

            if (_rep._bodySize != 0)
            {
                std::cout << "get cgi return data :" << std::endl
                          << _rep._response_body << std::flush; // 查看一下获取的结果
            }

            int status = 0;
            int ret = waitpid(pid, &status, 0);
            if (WIFEXITED(status)) // 如果正常退出，不是信号结束
            {
                if (WEXITSTATUS(status) == 0) // 读取一下返回值，如果返回值为0
                {
                    code = OK;
                }
                else
                {
                    std::cout << "Bad_Request" << std::endl;
                    code = Bad_Request;
                }
            }
            else // 由信号结束
            {
                std::cout << "Server_Error" << std::endl;
                code = Server_Error;
            }

            // 等待完之后把创建的所有的管道的文件描述符全部关闭,减少网络资源浪费
            close(input[0]);
            close(output[1]); // 这里如果是POST方法会第二次调用，会返回一个错误码EBADF
        }
        else // 进程创建错误
        {
            lg(ERROR, "fork fail");
            return Server_Error;
        }
        return code;
    }

    void buildOkResponse() // 200状态码的响应报头编写
    {
        // 响应的报头
        std::string hand_line;
        hand_line = "Content-Type: " + suffixToType(_req._suffix) + Rseponse_End;
        _rep._response_head.push_back(hand_line);
        hand_line = "Content-Length: " + std::to_string(_rep._bodySize) + Rseponse_End;
        _rep._response_head.push_back(hand_line);
    }

    void buildErrorResponse(const std::string &path) // 404状态码的响应报头编写
    {
        _req._cgi = false; // 只要是进入了错误报头编写函数，就只返回静态页面，所以不能使用cgi的方式发送body(小细节)
        // 打开404时响应的文件
        _rep._fd = open(path.c_str(), O_RDONLY);
        if (_rep._fd < 0)
        {
            lg(WARNING, "open %s fail!", path.c_str());
            return;
        }
        struct stat st;
        stat(path.c_str(), &st);
        _rep._bodySize = st.st_size;
        // 响应的报头
        std::string hand_line;
        hand_line = "Content-Type: text/html" + Rseponse_End;
        _rep._response_head.push_back(hand_line);
        hand_line = "Content-Length: " + std::to_string(_rep._bodySize) + Rseponse_End;
        _rep._response_head.push_back(hand_line);
    }

    void buildResponseHeader() // 不同状态码的响应报头进行处理
    {
        // 初始化状态行属性
        _rep._version = Http_Version;
        _rep._code_describe = codeToDescribe(atoi(_rep._code.c_str()));
        // 添加属性进入首行,创建好首行
        _rep._status_line = _rep._version;
        _rep._status_line += " ";
        _rep._status_line += _rep._code;
        _rep._status_line += " ";
        _rep._status_line += _rep._code_describe;
        _rep._status_line += Rseponse_End;

        // 响应的空行
        _rep._blank = Rseponse_End;

        // 首行需要根据不同的状态码进行不同的处理

        switch (atoi(_rep._code.c_str()))
        {
        case 200:
            buildOkResponse();
            break;
        case 404:
            buildErrorResponse("../webRoot/404.html");
            break;
        case 400:
            buildErrorResponse("../webRoot/400.html");
            break;
        default:
            buildErrorResponse("../webRoot/404.html"); // 可以根据不同状态码返回不同的页面错误响应构建函数
            break;
        }
    }

public:
    bool _stop;

    Dispose(int sock) : _sock(sock), _stop(false) {}

    ~Dispose()
    {
        Close(); // 处理好一次通讯后要记得关闭链接
    }

    int getSock()
    {
        return _sock;
    }

    void Close()
    {
        close(_sock);
    }

    void recvHttpRequest()
    {
        // 将报文读取到请求信息中(序列化)
        if ((!recvHttpRequestLine()) && (!recvHttpRequestHead()))
        {
            // 只有对请求行和报头都成功处理时才可以继续对请求进行处理
            parseHttpRequestLine();
            parseHttpRequestHead();

            recvHttpRequestBody(); // 处理正文部分
        }
    }

    void buildHttpResponse()
    {
        if (_req._method == "POST" || _req._method == "GET")
        {
            std::string &uri = _req._uri;
            std::string &path = _req._path;
            std::string &attribute = _req._submit_attribute;
            if (_req._method == "GET")
            {
                if (uri.find("?") == std::string::npos)
                {
                    path = uri;
                }
                else
                {
                    Util::cutString(_req._uri, path, attribute, "?");
                    _req._cgi = true;
                }

                // // debug
                // lg(DEBUG, "_req._path:%s  _req._submit_attribute:%s", _req._path.c_str(), _req._submit_attribute.c_str());
            }
            else if (_req._method == "POST")
            {
                _req._cgi = true;
                path = _req._uri;
            }

            // 下面对path进行处理，定位到我们的web根目录
            std::string webPath(WebRoot);
            if (path == "/" || path == "")
            {
                webPath += HomePage; // 默认访问主页
                path = webPath;
            }
            else
            {
                webPath += path;
                path = webPath;
            }

            // debug
            lg(DEBUG, "want interview webpath: %s", path.c_str());

            // 下面对定位好的path进行判断，检查path路径是否合法(1.文件夹2.可执行程序3.不存在的文件)
            struct stat st; // 用来存储文件属性的结构体

            if (stat(path.c_str(), &st) == 0) // 如果path路径存在
            {
                if (S_ISDIR(st.st_mode)) // 1.路径文件是一个文件夹
                {
                    path += HomePage;        // 更改目标路径文件
                    stat(path.c_str(), &st); // 重新获取一次文件属性
                    lg(DEBUG, "path :%s", path.c_str());
                }
                else if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)) // 2.路径文件是一个可执行程序
                {
                    // 特殊处理
                    _req._cgi = true;
                }
                _rep._bodySize = st.st_size; // 响应的文件的大小
                lg(DEBUG, "_rep._bodySize:%d", _rep._bodySize);
            }
            else // 3.不存在此文件
            {
                lg(WARNING, "path not find!");

                perror("stat failed");
                _rep._code = Not_Find;
                goto END;
            }

            // 通过path获取后缀
            int pos = path.rfind('.');
            if (pos == std::string::npos)
            {
                _req._suffix = ".html";
            }
            else
            {
                _req._suffix = path.substr(pos); // 带着'.'一起截
            }

            // 判断是否使用CGI机制来进行数据处理
            if (_req._cgi)
            {
                _rep._code = cgiToRequest(); // cgi用来将数据获取到responsebody中
            }
            else
            {
                _rep._code = nonCgiToRequest();
            }
        }
        else // 其他请求方法
        {
            lg(WARNING, "metheod can't handle !");
            _rep._code = Bad_Request;
            // 其他请求方法处理
            goto END;
        }

    END:
        buildResponseHeader(); // 构建响应报头

        return;
    }
    void sendHttpResponse()
    {
        send(_sock, _rep._status_line.c_str(), _rep._status_line.size(), 0); // 发送首行到内核缓冲区
        for (auto &it : _rep._response_head)
        {
            send(_sock, it.c_str(), it.size(), 0); // 发送响应报头行到内核缓冲区
        }
        send(_sock, _rep._blank.c_str(), _rep._blank.size(), 0); // 发送空行到内核缓冲区

        if (_req._cgi)
        {
            // send(_sock, _rep._response_body.c_str(), _rep._response_body.size(), 0);//直接这么可能存在发送缓冲区不足的情况
            int size = 0;
            int total = 0;
            std::string &body = _rep._response_body;
            while ((total < body.size()) && (size = write(_sock, body.c_str() + total, body.size() - total)) > 0)
            {
                total += size;
            }
        }
        else
        {
            sendfile(_sock, _rep._fd, nullptr, _rep._bodySize); // 直接将文件通过套接字发送到远端
            close(_rep._fd);                                    // 将非cgi的文件关闭
        }
    }
};

// class Entrance
class CalBack
{
private:
public:
    void operator()(int sock)
    {
        HandleRequest(sock);
    }

    void HandleRequest(int sock)
    {
        // lg(NOTICE, "get a link sock:%d", sock);

#ifdef DEBUG
        char buffer[4096];
        int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        buffer[n] == '\0';
        cout << "----------------begin--------------------" << endl;
        cout << buffer;
        cout << "----------------end----------------------" << endl;
#endif
        lg(DEBUG, "---------------------------begin---------------------------------");
        std::shared_ptr<Dispose> disposer = std::make_shared<Dispose>(sock); // 防止内存泄漏
        disposer->recvHttpRequest();
        if (!disposer->_stop) // 对读取错误进行处理
        {
            // debug
            lg(NOTICE, "recv success! to build and send!");

            // 只有对报文请求没有处理出错的时候才需要向后进行响应的构建和发送
            disposer->buildHttpResponse();
            disposer->sendHttpResponse();
        }
        else
        {
            lg(NOTICE, "recv httprequest fail! close sock:%d !", disposer->getSock());
        }

        lg(DEBUG, "---------------------------end---------------------------------");
    }
};
